package com.sqbang.dbcompare.biz;

import com.sqbang.dbcompare.constant.SystemConstant;
import com.sqbang.dbcompare.constant.Whether;
import com.sqbang.dbcompare.pojo.bo.TableBriefBo;
import com.sqbang.dbcompare.pojo.bo.TableInfoBo;
import com.sqbang.dbcompare.pojo.cache.CommonData;
import com.sqbang.dbcompare.pojo.dto.DatabaseInfoDto;
import com.sqbang.dbcompare.pojo.model.BizException;
import com.sqbang.dbcompare.pojo.vo.GeneratorInfoVo;
import com.sqbang.dbcompare.util.Tools;
import org.apache.commons.io.FileUtils;
import org.beetl.core.BeetlKit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class GeneratorBiz {

    @Autowired
    DatabaseBiz databaseBiz;

    public boolean generateFile(GeneratorInfoVo param) {
        LocalDate.now();
        // 步骤1：判断参数是否合规
        DatabaseInfoDto databaseInfoDto = CommonData.databaseInfoList.get(param.getDatabaseKey());
        if (databaseInfoDto == null) {
            throw new BizException(HttpStatus.BAD_REQUEST, "数据库连接信息无效");
        }

        // 步骤2：梳理条件
        List<TableBriefBo> tableNameList = databaseBiz.listTableComment(param.getDatabaseKey());
        if (StringUtils.isEmpty(param.getTableName()) && tableNameList.size() > 200) {
            // 暂定限制不要超过200，表越多生成的时间花费就越多。考虑到每次不一定都生成那么多，而且分表的情况下数据库会有非常多重复的分表。
            throw new BizException(HttpStatus.BAD_REQUEST, "生成的表数量不能超过200张");
        }
        List<TableBriefBo> selectTableList = null;
        if (StringUtils.isEmpty(param.getTableName())) {
            selectTableList = tableNameList;
        } else {
            selectTableList = this.matchTableName(tableNameList, param.getTableName());
        }
        if (CollectionUtils.isEmpty(selectTableList)) {
            throw new BizException(HttpStatus.BAD_REQUEST, "没有数据库表需要生成文件");
        }

        // 步骤3：整理表结构信息
        List<TableInfoBo> tableValueList = databaseBiz.getTableInfo(param.getDatabaseKey(), selectTableList, param.getIgnoreTablePrefix());

        // 步骤4：生成文件
        this.createFile(tableValueList, param);
        return true;
    }

    /**
     * 匹配表的名字
     * @param allTableList
     * @param selectTableName
     * @return
     */
    private List<TableBriefBo> matchTableName(List<TableBriefBo> allTableList, String selectTableName) {
        Set<String> indexOfSet = new HashSet<>();
        Set<String> prefixEqualSet = new HashSet<>();
        Set<String> suffixEqualSet = new HashSet<>();
        Set<String> equalSet = new HashSet<>();

        String[] tableMatchArray = null;
        if (selectTableName.indexOf(",") != -1) {
            tableMatchArray = selectTableName.split(",");
        } else {
            tableMatchArray = new String[1];
            tableMatchArray[0] = selectTableName;
        }
        for (String tableMatch : tableMatchArray) {
            if (StringUtils.isEmpty(tableMatch)) {
                throw new BizException(HttpStatus.BAD_REQUEST, "逗号之间的表名不能为空");
            }
            if (tableMatch.indexOf("*") != -1) {
                String[] tableNameSection = tableMatch.split("\\*");
                if (tableNameSection.length >= 3) {
                    throw new BizException(HttpStatus.BAD_REQUEST, "*号匹配符不能超过2个");
                }
                if (tableNameSection.length == 2) {
                    String substring = tableMatch.substring(tableMatch.indexOf("*"), tableMatch.lastIndexOf("*"));
                    if (StringUtils.isEmpty(substring)) {
                        throw new BizException(HttpStatus.BAD_REQUEST, "匹配符不正确");
                    }
                    indexOfSet.add(substring);
                } else if (tableMatch.indexOf("*") == 0){
                    String substring = tableMatch.substring(1);
                    if (StringUtils.isEmpty(substring)) {
                        throw new BizException(HttpStatus.BAD_REQUEST, "匹配符不正确");
                    }
                    suffixEqualSet.add(substring);
                } else {
                    String substring = tableMatch.substring(0, tableMatch.length() - 1);
                    if (StringUtils.isEmpty(substring)) {
                        throw new BizException(HttpStatus.BAD_REQUEST, "匹配符不正确");
                    }
                    prefixEqualSet.add(substring);
                }
            } else {
                equalSet.add(tableMatch);
            }
        }

        List<TableBriefBo> resultTableList = new ArrayList<>();
        for (TableBriefBo table : allTableList) {
            String tableName = table.getTableName();
            if (equalSet.contains(tableName)) {
                resultTableList.add(table);
            } else {
                boolean isFound = false;
                if (!isFound && !CollectionUtils.isEmpty(prefixEqualSet)) {
                    for (String pre : prefixEqualSet) {
                        if (tableName.indexOf(pre) == 0) {
                            resultTableList.add(table);
                            isFound = true;
                            break;
                        }
                    }
                }
                if (!isFound && !CollectionUtils.isEmpty(suffixEqualSet)) {
                    for (String suf : suffixEqualSet) {
                        if (tableName.lastIndexOf(suf) == tableName.length() - suf.length()) {
                            resultTableList.add(table);
                            isFound = true;
                            break;
                        }
                    }
                }
                if (!isFound && !CollectionUtils.isEmpty(indexOfSet)) {
                    for (String idx : indexOfSet) {
                        if (tableName.lastIndexOf(idx) != -1) {
                            resultTableList.add(table);
                            isFound = true;
                            break;
                        }
                    }
                }
            }
        }
        return resultTableList;
    }

    /**
     * 生成代码文件
     * @param tableValueList
     * @param param
     */
    private void createFile(List<TableInfoBo> tableValueList, GeneratorInfoVo param) {
        String targetFilePath = param.getFilePath();
        if (targetFilePath == null || targetFilePath.length() == 0) {
            targetFilePath = System.getProperty("user.dir") + File.separator + SystemConstant.FilePath.DATA_DISK_DIR;
        }
        targetFilePath += File.separator + "generatorFile" + System.currentTimeMillis();
        for (TableInfoBo tableInfoBo : tableValueList) {
            List<TableInfoBo.FieldInfoBo> collect = tableInfoBo.getFieldList().stream().filter(e -> e.getFieldValuePackage() != "").distinct().collect(Collectors.toList());
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("dataTypeList", collect.stream().map(TableInfoBo.FieldInfoBo::getFieldValuePackage).distinct().collect(Collectors.toList()));
            paramMap.put("tableInfoBo", tableInfoBo);
            paramMap.put("packageControllerName", param.getPackageControllerName());
            paramMap.put("packageEntityName", param.getPackageEntityName());
            paramMap.put("packageMapperName", param.getPackageMapperName());
            paramMap.put("packageMapperXmlName", param.getPackageMapperXmlName());
            paramMap.put("packageServiceImplName", param.getPackageServiceImplName());
            paramMap.put("packageServiceName", param.getPackageServiceName());
            if (param.getIsGenController()) {
                this.createFileByTemplate(targetFilePath, "controller", paramMap, tableInfoBo.getTableUpperCaseName() + "Controller.java");
            }
            if (param.getIsGenEntity()) {
                this.createFileByTemplate(targetFilePath, "entity", paramMap, tableInfoBo.getTableUpperCaseName() + ".java");
            }
            if (param.getIsGenMapper()) {
                this.createFileByTemplate(targetFilePath, "mapper", paramMap, tableInfoBo.getTableUpperCaseName() + "Mapper.java");
            }
            if (param.getIsGenMapperXml()) {
                this.createFileByTemplate(targetFilePath, "mapperXml", paramMap, tableInfoBo.getTableUpperCaseName() + "Mapper.xml");
            }
            if (param.getIsGenServiceImpl()) {
                this.createFileByTemplate(targetFilePath, "serviceImpl", paramMap, tableInfoBo.getTableUpperCaseName() + "ServiceImpl.java");
            }
            if (param.getIsGenService()) {
                this.createFileByTemplate(targetFilePath, "service", paramMap, "I" + tableInfoBo.getTableUpperCaseName() + "Service.java");
            }
        }
    }

    /**
     * 根据具体模板生成文件
     */
    private void createFileByTemplate(String targetPath, String templateFileName, Map<String, Object> paramMap, String codeFileFullName) {
        try {
            String allFileDir = targetPath + File.separator + "all";
            String codeFileDir = targetPath + File.separator + templateFileName;
            File allFilePathFile = new File(allFileDir);
            if (!allFilePathFile.exists() || !allFilePathFile.isDirectory()) {
                allFilePathFile.mkdirs();
            }
            File codeFilePathFile = new File(codeFileDir);
            if (!codeFilePathFile.exists() || !codeFilePathFile.isDirectory()) {
                codeFilePathFile.mkdirs();
            }

            InputStream inStream = GeneratorBiz.class.getResourceAsStream("/template/" + templateFileName + ".html");
            String templateContentStr = Tools.getStringFromInputString(inStream);

            String javaSrc = BeetlKit.render(templateContentStr, paramMap);

            String allFilePath = targetPath + File.separator + "all" + File.separator + codeFileFullName;
            String codeFilePath = targetPath + File.separator + templateFileName + File.separator + codeFileFullName;
            File allFile = new File(allFilePath);
            File codeFile = new File(codeFilePath);

            FileUtils.writeStringToFile(allFile, javaSrc, "UTF-8");
            FileUtils.writeStringToFile(codeFile, javaSrc, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
